package auth

import (
	"context"
	"crypto/rsa"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"inventory/internal/constant"
	"inventory/pkg/network"
	"inventory/pkg/token"
	"reflect"
)

const (
	WebTokenHeaderKey    = "B-Token"
	WebTokenMetaKey      = "b-token"
	customerTokenMetaKey = "authorization"
	WebTokenType         = "web"
	CustomerTokenType    = "customer"
)

type Config struct {
	WebPublicKey      string `mapstructure:"web_pub_key"`
	webPublicKey      *rsa.PublicKey
	CustomerPublicKey string `mapstructure:"customer_pub_key"`
	customerPublicKey *rsa.PublicKey
}

func (c *Config) InitSecret() (err error) {
	if c.webPublicKey, err = jwt.ParseRSAPublicKeyFromPEM([]byte(c.WebPublicKey)); err != nil {
		err = fmt.Errorf("jwt公钥初始化失败, error=%v", err)
		return
	}
	if c.customerPublicKey, err = jwt.ParseRSAPublicKeyFromPEM([]byte(c.CustomerPublicKey)); err != nil {
		err = fmt.Errorf("customer jwt公钥初始化失败, error=%v", err)
		return
	}
	return
}

// 根据对象格式匹配token
func (c *Config) TokenInterceptor(ctx context.Context, req interface{}) (context.Context, error) {
	var err error
	isHTTP, _, _ := network.GetHttpCallInfo(ctx)
	if webTokenRequest, isWebTokenRequest := req.(WebTokenRequest); isWebTokenRequest {
		rawToken := webTokenRequest.GetWebToken()
		if rawToken == nil || rawToken.EmpId == 0 || rawToken.SgId == 0 {
			if !isHTTP {
				return ctx, constant.ErrUnauthenticated
			}
			webToken := &WebToken{}
			if ctx, err = token.InitRPCToken(
				ctx, WebTokenType, WebTokenMetaKey, c.webPublicKey, webToken); err == nil {
				v := reflect.ValueOf(req).Elem()
				v.FieldByName("WebToken").Set(reflect.ValueOf(webToken.WebToken))
			}
			return ctx, err
		} else if isHTTP {
			// http仅允许通过header传输token
			return ctx, constant.ErrUnauthenticated
		}
	} else if _, isCustomerTokenRequest := req.(CustomerTokenRequest); isCustomerTokenRequest {
		if !isHTTP {
			// C端暂时只开放HTTP请求，不开放直接调用
			return ctx, constant.ErrUnauthenticated
		}
		// 直接反射塞入，不从req对象取值或者判定
		customerToken := &CustomerToken{}
		if ctx, err = token.InitRPCToken(
			ctx, CustomerTokenType, customerTokenMetaKey, c.customerPublicKey, customerToken); err == nil {
			v := reflect.ValueOf(req).Elem()
			v.FieldByName("CustomerToken").Set(reflect.ValueOf(customerToken.CustomerToken))
		}
		return ctx, err

	}
	return ctx, nil
}
